/**
 * Map.jsx
 * 3D地图主组件 - 负责处理地图的基本渲染、投影转换和布局
 */
import * as THREE from 'three';
import * as d3 from 'd3-geo';
import Base from './Base';
import Mid from './Mid';
import Top from './Top';
import { useEffect, useRef } from 'react';
import { useThree } from '@react-three/fiber';
import gsap from 'gsap';

/**
 * 数据预处理函数 - 规范化GeoJSON数据结构并添加值属性
 * @param {Object} oriData - 原始GeoJSON数据
 * @param {Object} values - 包含区域名称到数值的映射
 * @returns {Object} - 处理后的GeoJSON数据
 */
const processing = (oriData, values) => {
	oriData.features.forEach((region) => {
		// 为每个区域添加值属性
		region.properties.value = values.features[region.properties.name];
		// 修正坐标数据结构(处理嵌套层级不一致的问题)
		if (typeof region.geometry.coordinates[0][0][0] === 'number') {
			const temp = region.geometry.coordinates[0];
			region.geometry.coordinates[0] = [temp];
		}
	});
	return oriData;
};

/**
 * 3D地图组件
 * @param {number} baseHeight - 地图基础高度
 * @param {number} midHeightScale - 中间层高度比例
 * @param {number} topHeightScale - 顶层高度比例
 * @param {Object} values - 地图数值数据
 * @param {Object} geoJson - GeoJSON格式的地图几何数据
 * @param {Array} mapCenter - 地图中心坐标
 * @param {Function} onRegionClick - 区域点击事件回调，用于获取区域名称
 * @param {Function} onRegionDrillDown - 区域双击事件回调，用于地图下钻功能
 * @param {number} distanceFactor - HTML元素缩放因子，控制文本大小
 * @param {number} textYOffset - 文本Y轴偏移量，默认为-0.5
 */
const Map = ({ baseHeight, midHeightScale, topHeightScale, values, geoJson, mapCenter, gsapCamera, onRegionClick, onRegionDrillDown, distanceFactor, textYOffset = -0.5 }) => {
	// 创建地图3D对象
	const map = new THREE.Object3D();
	// 创建墨卡托投影，用于将地理坐标转换为平面坐标
	const projection = d3.geoMercator().center(mapCenter).translate([0, 0]);
	// 创建对地图组的引用
	const groupRef = useRef();
	// 获取Three.js场景中的相机
	const { camera } = useThree();

	// 处理GeoJSON数据并生成地图几何体
	processing(geoJson, values).features.forEach(element => {
		// 为每个区域创建3D对象
		const region = new THREE.Object3D();
		const coordinates = element.geometry.coordinates;

		// 处理多边形坐标
		coordinates.forEach(multiPolygon => {
			multiPolygon.forEach((polygon) => {
				// 创建2D形状，用于后续挤出为3D
				const shape = new THREE.Shape();
				// 创建线条材质，用于边界线
				const lineMaterial = new THREE.LineBasicMaterial({
					color: '#ffffff',
				});
				// 创建边界线几何体
				const lineGeometry = new THREE.BufferGeometry();

				// 收集顶点数据
				const vertices = [];
				for (let i = 0; i < polygon.length; i++) {
					let x = null;
					let y = null;
					if (polygon[i] instanceof Array) {
						// 将地理坐标转换为平面坐标
						[x, y] = projection(polygon[i]);
						if (i === 0) {
							shape.moveTo(x, -y);
						}
						shape.lineTo(x, -y);
						// 存储顶点位置，用于创建边界线
						vertices.push(x, -y, baseHeight * topHeightScale + 0.001);
					}
				}
				// 设置顶点属性
				lineGeometry.setAttribute('position', new THREE.BufferAttribute(new Float32Array(vertices), 3));

				// 挤出设置，用于将2D形状转为3D模型
				const extrudeSettings = {
					depth: baseHeight,
					bevelEnabled: false, // 不使用斜角
				};

				// 创建挤出几何体、材质和网格
				const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings);
				const material = new THREE.MeshBasicMaterial();
				const mesh = new THREE.Mesh(geometry, material);
				const line = new THREE.Line(lineGeometry, lineMaterial);
				// 添加到区域对象
				region.add(mesh);
				region.add(line);
			});
		});

		// 保存区域属性数据
		region.properties = element.properties;
		if (element.properties.centroid) {
			// 转换中心点坐标并保存
			const [x, y] = projection(element.properties.centroid);
			region.properties._centroid = [x, y];
		}

		// 将区域添加到地图对象
		map.add(region);
	});

	// 计算地图边界并居中 - 在组件挂载后执行
	useEffect(() => {
		if (groupRef.current) {
			// 创建一个临时的包围盒来计算地图的边界
			const box = new THREE.Box3().setFromObject(groupRef.current);
			const center = new THREE.Vector3();
			box.getCenter(center);
			
			// 将地图移动到场景中心
			groupRef.current.position.set(-center.x, -center.y, -center.z);
			
			// 调整相机视角以便完整查看地图
			const size = box.getSize(new THREE.Vector3());
			const maxDim = Math.max(size.x, size.y, size.z);
			const fov = camera.fov * (Math.PI / 180);
			const cameraDistance = maxDim / (2 * Math.tan(fov / 2));
			
			// 自适应调整相机位置和角度
			if (cameraDistance > 0) {
				// 计算合适的相机位置
				const idealDistance = cameraDistance * 1.2; // 增加一点缓冲距离
				
				// 使用GSAP动画平滑过渡到新的相机位置
				gsap.to(camera.position, {
					duration: 1.2,
					x: gsapCamera.x,
					y: idealDistance * gsapCamera.y,
					z: idealDistance * gsapCamera.z,
					ease: "power2.inOut",
					onUpdate: () => {
						// 确保相机始终看向中心点
						camera.lookAt(0, 0, 0);
					}
				});
			}
		}
	}, [camera, geoJson]); // 添加geoJson作为依赖，使地图切换时触发重新计算

	return (
		<group ref={groupRef}>
			{/* 地图基础层 */}
			<Base blocks={map.children} baseHeight={baseHeight} />
			{/* 地图中间层 */}
			<Mid blocks={map.children} baseHeight={baseHeight} midHeightScale={midHeightScale} />
			{/* 地图顶层(包含交互) */}
			<Top
				blocks={map.children}
				baseHeight={baseHeight}
				midHeightScale={midHeightScale}
				topHeightScale={topHeightScale}
				values={values}
				mapCenter={mapCenter}
				onRegionClick={onRegionClick}
				onRegionDrillDown={onRegionDrillDown}
				distanceFactor={distanceFactor}
				textYOffset={textYOffset}
			/> 
		</group>
	);
};

export default Map;
